// CaretTracking.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "CaretTracking.h"

#define MAX_LOADSTRING 100

#include <Stdio.h>

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);

VOID CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook,
				DWORD event,
				HWND hwnd,
				LONG idObject,
				LONG idChild,
				DWORD dwEventThread,
				DWORD dwmsEventTime);

HWINEVENTHOOK	ghLocationChangeHook	= NULL;
HWINEVENTHOOK	ghObjectCreationHook	= NULL;
HWINEVENTHOOK	ghObjectDestructionHook	= NULL;
HWINEVENTHOOK	ghObjectShowHook		= NULL;
HWINEVENTHOOK	ghObjectHideHook		= NULL;
HWINEVENTHOOK	ghObjectFocusHook		= NULL;

HWND	ghWnd = NULL;

#define WM_UPDATECAREPOS	(WM_USER + 1234)
#define WM_CARETDEAD		(WM_USER + 1235)

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_CARETTRACKING, szWindowClass, MAX_LOADSTRING);
	MyRegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) 
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_CARETTRACKING);

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    This function and its usage are only necessary if you want this code
//    to be compatible with Win32 systems prior to the 'RegisterClassEx'
//    function that was added to Windows 95. It is important to call this function
//    so that the application will get 'well formed' small icons associated
//    with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_CARETTRACKING);
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);

	return RegisterClassEx(&wcex);
}

//
//   FUNCTION: InitInstance(HANDLE, int)
//
//   PURPOSE: Saves instance handle and creates main window
//
//   COMMENTS:
//
//        In this function, we save the instance handle in a global variable and
//        create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // Store instance handle in our global variable

   ghWnd = CreateWindowEx(WS_EX_TOPMOST, szWindowClass, szTitle, WS_CAPTION|WS_SYSMENU|WS_VISIBLE|WS_MINIMIZEBOX,
      CW_USEDEFAULT, 0, 320, 128, NULL, NULL, hInstance, NULL);

   if (!ghWnd)
   {
      return FALSE;
   }

   ShowWindow(ghWnd, nCmdShow);
   UpdateWindow(ghWnd);

   return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) 
	{
	case WM_CARETDEAD:
		{
			SetWindowText(hWnd, _T("DEAD !!"));
			break;
		}
	case WM_UPDATECAREPOS:
		{
			TCHAR sz[1024];

			_stprintf(sz, _T("Pos = { %d, %d }"), LOWORD(lParam), HIWORD(lParam) );

			SetWindowText(hWnd, sz);

			break;
		}
	case WM_CREATE:
		{
			ghLocationChangeHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,EVENT_OBJECT_LOCATIONCHANGE,
						NULL,
						WinEventProc,
						NULL,			// Receive Events From All Processes
						NULL,			// Receive Events From All Threads
						WINEVENT_OUTOFCONTEXT /*|WINEVENT_SKIPOWNPROCESS| WINEVENT_SKIPOWNTHREAD*/);
			ghObjectCreationHook = SetWinEventHook(EVENT_OBJECT_CREATE,EVENT_OBJECT_CREATE,
						NULL,
						WinEventProc,
						NULL,			// Receive Events From All Processes
						NULL,			// Receive Events From All Threads
						WINEVENT_OUTOFCONTEXT /*|WINEVENT_SKIPOWNPROCESS| WINEVENT_SKIPOWNTHREAD*/);
			ghObjectDestructionHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,EVENT_OBJECT_LOCATIONCHANGE,
						NULL,
						WinEventProc,
						NULL,			// Receive Events From All Processes
						NULL,			// Receive Events From All Threads
						WINEVENT_OUTOFCONTEXT /*|WINEVENT_SKIPOWNPROCESS| WINEVENT_SKIPOWNTHREAD*/);
			ghObjectShowHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,EVENT_OBJECT_LOCATIONCHANGE,
						NULL,
						WinEventProc,
						NULL,			// Receive Events From All Processes
						NULL,			// Receive Events From All Threads
						WINEVENT_OUTOFCONTEXT /*|WINEVENT_SKIPOWNPROCESS| WINEVENT_SKIPOWNTHREAD*/);
			ghObjectHideHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,EVENT_OBJECT_LOCATIONCHANGE,
						NULL,
						WinEventProc,
						NULL,			// Receive Events From All Processes
						NULL,			// Receive Events From All Threads
						WINEVENT_OUTOFCONTEXT /*|WINEVENT_SKIPOWNPROCESS| WINEVENT_SKIPOWNTHREAD*/);
			ghObjectFocusHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE,EVENT_OBJECT_LOCATIONCHANGE,
						NULL,
						WinEventProc,
						NULL,			// Receive Events From All Processes
						NULL,			// Receive Events From All Threads
						WINEVENT_OUTOFCONTEXT /*|WINEVENT_SKIPOWNPROCESS| WINEVENT_SKIPOWNTHREAD*/);

			if( ghLocationChangeHook == NULL ||
				ghObjectCreationHook == NULL ||
				ghObjectDestructionHook == NULL || 
				ghObjectShowHook == NULL || 
				ghObjectHideHook == NULL || 
				ghObjectFocusHook == NULL)
			{
				MessageBox(hWnd, _T("Unable to Install Hook !!"), _T("Error"), MB_OK|MB_ICONERROR);
				return -1;
			}

			break;
		}
	case WM_DESTROY:
		{
			if(ghLocationChangeHook)
			{
				UnhookWinEvent(ghLocationChangeHook);
				ghLocationChangeHook = NULL;
			}
			if(ghObjectCreationHook)
			{
				UnhookWinEvent(ghObjectCreationHook);
				ghObjectCreationHook = NULL;
			}
			if(ghObjectDestructionHook)
			{
				UnhookWinEvent(ghObjectDestructionHook);
				ghObjectDestructionHook = NULL;
			}
			if(ghObjectShowHook)
			{
				UnhookWinEvent(ghObjectShowHook);
				ghObjectShowHook = NULL;
			}
			if(ghObjectHideHook)
			{
				UnhookWinEvent(ghObjectHideHook);
				ghObjectHideHook = NULL;
			}
			if(ghObjectFocusHook)
			{
				UnhookWinEvent(ghObjectFocusHook);
				ghObjectFocusHook = NULL;
			}
			PostQuitMessage(0);
			break;
		}
	case WM_PAINT:
		{
			RECT rect;
			GetClientRect(hWnd, &rect);
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hWnd, &ps);
			DrawText(hdc, _T("Open NotePad and observe the title of this window"), 49, &rect, DT_CENTER|DT_WORDBREAK);
			EndPaint(hWnd, &ps);
			break;
		}
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}


VOID CALLBACK WinEventProc(HWINEVENTHOOK hWinEventHook,
				DWORD event,
				HWND hwnd,
				LONG idObject,
				LONG idChild,
				DWORD dwEventThread,
				DWORD dwmsEventTime)
{
	DWORD OtherThreadID = GetWindowThreadProcessId(GetForegroundWindow(), NULL);

	GUITHREADINFO guiThreadInfo; 
	guiThreadInfo.cbSize = sizeof(GUITHREADINFO);
	
	if(GetGUIThreadInfo(OtherThreadID, &guiThreadInfo))
	{
		if(guiThreadInfo.hwndCaret != NULL)
		{
			POINT pt = { guiThreadInfo.rcCaret.left, guiThreadInfo.rcCaret.top };
			
			ClientToScreen(guiThreadInfo.hwndCaret, &pt);
			
			SendMessage(ghWnd, WM_UPDATECAREPOS, NULL, MAKELPARAM(pt.x, pt.y));
		}
		else
			SendMessage(ghWnd, WM_CARETDEAD, NULL, NULL);
	}
	return;
}